package com.tmall.testgen.server.agent;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.Map;

import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

import com.tmall.testgen.common.ConnectionCommond;
import com.tmall.testgen.common.util.SocketUtil;
import com.tmall.testgen.common.util.StringUtil;
import com.tmall.testgen.server.infocollect.DataCenter;
import com.tmall.testgen.server.infocollect.InfoCollectListener;
import com.tmall.testgen.server.infocollect.UserData;
import com.tmall.testgen.server.socket.SocketServer;

/**
 * @author haojia
 * @date 2014-07-04
 */
public class Transformer4AOP implements ClassFileTransformer{

	/**
	 * 上一次加载的类，用于寻找类加载器
	 */
	public static  Class lastLoadedClass;
	
	/**
	 * 加载同步，当收到用户的请求信息后才开始拦截，以保证只拦截有效信息，成功接收到用户请求信息后调用
	 */
	private static Object loadSyn=new Object();
	
	/**
	 * 拦截同步
	 */
	private static Object agentSyn=new Object();
	
	/**
	 * 判断当前类是否需要处理
	 * @param className
	 * @return
	 */
	private static  boolean isClassHandle(String className){
		Map<String, UserData>  userDatas=DataCenter.getUserDatas();
		for(String userId:userDatas.keySet()){
			boolean userInterest=StringUtil.isMatchClass(userDatas.get(userId).getInterceptDes(), className)||StringUtil.isMatchClass(userDatas.get(userId).getMockDes(), className);
			boolean loaded=DataCenter.isClassLoaded(className);
			boolean isGenerate=className.contains("$");
			//包含$的为代理类 不过滤
			if(userInterest&&!loaded&&!isGenerate){
				//System.out.println(className+"成功拦截");
				return true;
			}
		}
		
		return false;
	}
	
	
	
	/**
	 * 此处会等待直到agent结束
	 * @throws InterruptedException 
	 */
	public static void continueRetransform() throws InterruptedException{
		synchronized (loadSyn) {
			loadSyn.notifyAll();
		}
		synchronized (agentSyn) {
			agentSyn.wait();
		}
		
		
	}
	
	public static void agentmain(String args, Instrumentation inst)throws Exception{ 
		
		if(!DataCenter.isStart){
			if(SocketUtil.isPortAvailable(ConnectionCommond.PORT)){
				System.out.println("端口未占用");
				DataCenter.isStart=true;
				SocketServer.startServer();
				//未加载的添加监听
				inst.addTransformer(new Transformer4AOP(),true);
			}else{
				System.out.println("端口被占用");
				return;
			}
		}else{
			System.out.println("端口在当前VM已经启动");
		}
		
		synchronized (loadSyn) {
			System.out.println("开始 loadSyn.wait()");
			loadSyn.wait();
			
		}
		
		int i=0;
         //已经加载的类开始拦截
        for (Class clazz :inst.getAllLoadedClasses()){ 
            if(lastLoadedClass==null){
                lastLoadedClass=clazz;
                synchronized (agentSyn) {
                    agentSyn.notifyAll();
                    //System.out.println("释放：agentSyn  transform");
                }
            }
            
            
        		try{
        			if(isClassHandle(clazz.getName())){
        				i++;
        				inst.retransformClasses(clazz);
        			}
        		}catch(Exception e){
        			e.printStackTrace();
        		}
        		
        } 
        
        //System.out.println("匹配数量："+i);
       
        if(lastLoadedClass!=null||i==0){
        	//停止职位找到虚拟机中的类，如果不为空表示已经找到，
        	synchronized (agentSyn) {
				agentSyn.notifyAll();
				//System.out.println("释放agentSy i==0");
			}
        }
        	
        
		
		
    }  


	@Override
	public byte[] transform(ClassLoader loader, String className,
			Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
			byte[] classfileBuffer) throws IllegalClassFormatException {
		
		try {
			
			String classStandardName=className.replaceAll("/", ".");
			if(!isClassHandle(classStandardName)){
				return null;
			}
			return	rewriteClass(loader, classStandardName, classBeingRedefined, protectionDomain, classfileBuffer);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	
	
	public byte[] rewriteClass(ClassLoader loader, String classStandardName,
			Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
			byte[] classfileBuffer) throws Exception{
		
		// 用于取得字节码类，必须在当前的classpath中，使用全称
		
		  ClassPool pool = ClassPool.getDefault();
		  if(classBeingRedefined==null){
			  if(lastLoadedClass==null){
				  return null;
			  }else{
				  pool.insertClassPath(new ClassClassPath(lastLoadedClass));
			  }
		  }else{
			  pool.insertClassPath(new ClassClassPath(classBeingRedefined)); 
		  }
		  
		  lastLoadedClass=classBeingRedefined;
			  
		
		
		CtClass  ctClass=	pool.get(classStandardName);
		CtMethod[]  methods=ctClass.getDeclaredMethods();
		
		if(methods==null||methods.length==0)
			return null;
		
		int methodCount=0;
		for(CtMethod method:methods){
			//handle(ctClass,method);
			handleMethod(classStandardName,method);
			methodCount++;
		}
		
		if(methodCount==0){
			DataCenter.addLoadedClassAndMethods(classStandardName,null);
		}
		   
        return ctClass.toBytecode();
		
		
	}
	
	public void handleMethod(String   classStandardName,CtMethod method) throws Exception{
		
		MethodInfo methodInfo = method.getMethodInfo(); 
		if(methodInfo.getAccessFlags()!=AccessFlag.PUBLIC)
			return;
		
		CodeAttribute codeAttribute = methodInfo.getCodeAttribute();  
	    LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); 
	        
		int length = method.getParameterTypes().length;  
		String paramNames="";
		int pos = Modifier.isStatic(method.getModifiers()) ? 0 : 1; 
		if(attr!=null){
	        for (int i = 0; i < length; i++)  {
	        	paramNames+=attr.variableName(i + pos)+";"; 
	        }
        }
        
        CtClass[]  types=method.getParameterTypes();
        int len=types==null?0:types.length;
        String[] ty=new String[len];
        String tyStr="";
        if(types!=null){
        	for(int i=0;i<types.length;i++){
        		ty[i]=types[i].getSimpleName();
        		tyStr+=types[i].getSimpleName()+";";
        	}
	        
        }
        
        String returnType= method.getReturnType().getSimpleName();
        DataCenter.addLoadedClassAndMethods(classStandardName, method.getName()+InfoCollectListener.getMethodParamContentContent(paramNames.split(";"), ty,returnType));
        
        method.insertAfter("{ com.tmall.testgen.server.infocollect.InfoCollectListener.addIntercept("+classStandardName+".class,\""+method.getName()+"\", \""+paramNames+"\", \""+tyStr+"\",\""+returnType+"\",$args,($w)$_); }");
		
	}

}
